/*******************************************************************************
* Copyright (c) 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.accessibility;
import java.util.*;
import org.eclipse.swt.internal.cocoa.*;
import org.eclipse.swt.widgets.*;
/**
* This class is used to describe a table for objects that have an accessible
* role of ACC.ROLE_TABLE, but aren't implemented like NSTableViews. That means they
* report back their children as a list of cells in row-major order instead of a list of
* rows with cells as children of those rows. The assumption is that the first 'row'
* of cells (cell 0 to cell 'column-count - 1') are the column headers of the table.
* Note that this class is NOT a subclass of Accessible, because it is not a child of the
* table accessible. It exists to factor out the logic when working with a lightweight table
* that doesn't conform to Cocoa's expectations. Specifically, it reports back a list of row
* and column objects and a header as its children, and identifies which cell the mouse is
* over.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
class TableAccessibleDelegate {
Map /*<Integer, AccessibleTableColumn>*/ childColumnToIdMap = new HashMap();
Map /*<Integer, AccessibleTableRow>*/ childRowToIdMap = new HashMap();
Accessible tableAccessible;
AccessibleTableHeader headerAccessible;
public TableAccessibleDelegate(Accessible accessible) {
tableAccessible = accessible;
tableAccessible.addAccessibleControlListener(new AccessibleControlAdapter() {
public void getChildCount(AccessibleControlEvent e) {
/* Return the number of row and column children.
* If the CTable2 is being used as a list (i.e. if columns.length == 0) then add 1 column child.
* If there is a column header (i.e. if columns.length > 0) then add 1 "column header" child.
*/
e.detail = childColumnToIdMap.size() + childRowToIdMap.size();
if (childColumnToIdMap.size() > 1) e.detail++;
}
public void getChildren(AccessibleControlEvent e) {
int childCount = childColumnToIdMap.size() + childRowToIdMap.size();
if (childColumnToIdMap.size() > 1) childCount++;
Accessible[] children = new Accessible[childCount];
int childIndex = 0;
Iterator iter = childRowToIdMap.values().iterator();
while (iter.hasNext()) {
AccessibleTableRow row = (AccessibleTableRow)iter.next();
children[childIndex++] = row;
}
iter = childColumnToIdMap.values().iterator();
while (iter.hasNext()) {
AccessibleTableColumn col = (AccessibleTableColumn)iter.next();
children[childIndex++] = col;
}
if (childColumnToIdMap.size() > 1) children[childIndex] = headerAccessible();
e.children = children;
}
public void getChildAtPoint(AccessibleControlEvent e) {
NSPoint testPoint = new NSPoint();
testPoint.x = e.x;
Monitor primaryMonitor = Display.getCurrent().getPrimaryMonitor();
testPoint.y = (int) (primaryMonitor.getBounds().height - e.y);
Iterator iter = childRowToIdMap.values().iterator();
while (iter.hasNext()) {
AccessibleTableRow row = (AccessibleTableRow) iter.next();
NSValue locationValue = new NSValue(row.getPositionAttribute(ACC.CHILDID_SELF).id);
NSPoint location = locationValue.pointValue();
NSValue sizeValue = new NSValue(row.getSizeAttribute(ACC.CHILDID_SELF));
NSSize size = sizeValue.sizeValue();
if (location.y < testPoint.y && testPoint.y < (location.y + size.height)) {
AccessibleControlEvent e2 = new AccessibleControlEvent(e.getSource());
e2.x = (int) testPoint.x;
e2.y = (int) testPoint.y;
row.getChildAtPoint(e);
break;
}
}
}
public void getState(AccessibleControlEvent e) {
int state = ACC.STATE_NORMAL | ACC.STATE_FOCUSABLE | ACC.STATE_SELECTABLE;
AccessibleTableEvent event = new AccessibleTableEvent(this);
for (int i = 0; i < tableAccessible.accessibleTableListeners.size(); i++) {
AccessibleTableListener listener = (AccessibleTableListener)tableAccessible.accessibleTableListeners.elementAt(i);
listener.getSelectedRows(event);
}
if (event.selected != null) {
int[] selected = (int[])event.selected;
for (int i = 0; i < selected.length; i++) {
if (selected[i] == tableAccessible.index) {
state |= ACC.STATE_SELECTED;
break;
}
}
}
NSNumber focusedObject = (NSNumber)tableAccessible.getFocusedAttribute(ACC.CHILDID_SELF);
if (focusedObject.boolValue()) {
state |= ACC.STATE_FOCUSED;
}
e.detail = state;
}
});
tableAccessible.addAccessibleTableListener(new AccessibleTableAdapter() {
public void getColumnCount(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
for (int i = 0; i < tableAccessible.accessibleTableListeners.size(); i++) {
AccessibleTableListener listener = (AccessibleTableListener)tableAccessible.accessibleTableListeners.elementAt(i);
if (listener != this) listener.getColumnCount(event);
}
e.count = event.count;
}
public void getColumn(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
getColumns(event);
e.accessible = event.accessibles[e.column];
}
public void getColumns(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
getColumnCount(event);
// This happens if the table listeners just report back a column count but don't have
// distinct objects for each of the column.
// When that happens we need to make 'fake' accessibles that represent the rows.
if (event.count != childColumnToIdMap.size()) {
childColumnToIdMap.clear();
}
Accessible[] columns = new Accessible[event.count];
for (int i = 0; i < event.count; i++) {
columns[i] = childColumnToOs(i);
}
int columnCount = childColumnToIdMap.size() > 0 ? childColumnToIdMap.size() : 1;
Accessible[] accessibles = new Accessible[columnCount];
for (int i = 0; i < columnCount; i++) {
accessibles[i] = (Accessible) childColumnToIdMap.get(new Integer(i));
}
e.accessibles = accessibles;
}
public void getColumnHeader(AccessibleTableEvent e) {
e.accessible = (childColumnToIdMap.size() > 1 ? headerAccessible() : null);
}
public void getRowCount(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
for (int i = 0; i < tableAccessible.accessibleTableListeners.size(); i++) {
AccessibleTableListener listener = (AccessibleTableListener)tableAccessible.accessibleTableListeners.elementAt(i);
if (listener != this) listener.getRowCount(event);
}
e.count = event.count;
}
public void getRow(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
getRows(event);
e.accessible = event.accessibles[e.row];
}
public void getRows(AccessibleTableEvent e) {
AccessibleTableEvent event = new AccessibleTableEvent(this);
getRowCount(event);
// This happens if the table listeners just report back a column count but don't have
// distinct objects for each of the column.
// When that happens we need to make 'fake' accessibles that represent the rows.
if (event.count != childRowToIdMap.size()) {
childRowToIdMap.clear();
}
Accessible[] rows = new Accessible[event.count];
for (int i = 0; i < event.count; i++) {
rows[i] = childRowToOs(i);
}
int columnCount = childRowToIdMap.size() > 0 ? childRowToIdMap.size() : 1;
Accessible[] accessibles = new Accessible[columnCount];
for (int i = 0; i < columnCount; i++) {
accessibles[i] = (Accessible) childRowToIdMap.get(new Integer(i));
}
e.accessibles = accessibles;
}
});
}
Accessible childColumnToOs(int childID) {
if (childID == ACC.CHILDID_SELF) {
return tableAccessible;
}
/* Check cache for childID, if found, return corresponding osChildID. */
AccessibleTableColumn childRef = (AccessibleTableColumn) childColumnToIdMap.get(new Integer(childID));
if (childRef == null) {
childRef = new AccessibleTableColumn(tableAccessible, childID);
childColumnToIdMap.put(new Integer(childID), childRef);
}
return childRef;
}
Accessible childRowToOs(int childID) {
if (childID == ACC.CHILDID_SELF) {
return tableAccessible;
}
/* Check cache for childID, if found, return corresponding osChildID. */
AccessibleTableRow childRef = (AccessibleTableRow) childRowToIdMap.get(new Integer(childID));
if (childRef == null) {
childRef = new AccessibleTableRow(tableAccessible, childID);
childRowToIdMap.put(new Integer(childID), childRef);
}
return childRef;
}
AccessibleTableHeader headerAccessible() {
if (headerAccessible == null) headerAccessible = new AccessibleTableHeader(tableAccessible, ACC.CHILDID_SELF);
return headerAccessible;
}
void release() {
if (childRowToIdMap != null) {
Collection delegates = childRowToIdMap.values();
Iterator iter = delegates.iterator();
while (iter.hasNext()) {
SWTAccessibleDelegate childDelegate = ((Accessible)iter.next()).delegate;
if (childDelegate != null) {
childDelegate.internal_dispose_SWTAccessibleDelegate();
childDelegate.release();
}
}
childRowToIdMap.clear();
childRowToIdMap = null;
}
if (childColumnToIdMap != null) {
Collection delegates = childColumnToIdMap.values();
Iterator iter = delegates.iterator();
while (iter.hasNext()) {
SWTAccessibleDelegate childDelegate = ((Accessible)iter.next()).delegate;
if (childDelegate != null) {
childDelegate.internal_dispose_SWTAccessibleDelegate();
childDelegate.release();
}
}
childColumnToIdMap.clear();
childColumnToIdMap = null;
}
}
void reset() {
release();
childColumnToIdMap = new HashMap();
childRowToIdMap = new HashMap();
}
}